/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.misc.rmc.binding;

import com.sap.conn.jco.JCoRecordMetaData;
import com.sap.conn.jco.JCoTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vacoor.nothing.common.util.StringUtils;
import org.vacoor.nothing.misc.rmc.session.Session;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * RFC 映射方法执行类
 *
 * @author vacoor
 */
public abstract class MapperMethod<T> {
    private static final Logger LOG = LoggerFactory.getLogger(MapperMethod.class);

    protected final Class<?> mapperInterface; // 代理接口
    protected final Method method;            // 代理方法
    protected final Session session;

    public MapperMethod(Class<?> mapperInterface, Method method, Session session) {
        this.mapperInterface = mapperInterface;
        this.method = method;
        this.session = session;
    }

    /**
     * 执行代理操作
     *
     * @param args
     * @return
     * @throws Throwable
     */
    public Object execute(Object[] args) throws Throwable {
        Class<?> retType = method.getReturnType();
        boolean retVoid = Void.class.isAssignableFrom(retType);
        boolean retMany = Iterable.class.isAssignableFrom(retType) || retType.isArray();
        boolean retMap = Map.class.isAssignableFrom(retType);
        boolean retOne = !retVoid && !retMany && !retMap;


        Object result = null;
        if (retVoid) {
            result = null;
        } else if (retMany) {
//            result = executeForMany(jcoTab, metaData);
        } else if (retMap) {    // 返回 map
//            result = executeForMap();
            throw new UnsupportedOperationException("does not support the return map object");
        } else {    // 其余情况认为返回单一对象
            throw new UnsupportedOperationException("does not support the return single object");
//            executeForMany()
            // if (result.size > 1) { throw } if (==1) return (0) else return null;
        }

        return result;
    }

    /**
     * 处理返回多条记录结果
     */
    protected Object executeForMany(JCoTable table, JCoRecordMetaData metaData) throws Throwable {
        List<Object> result = new ArrayList<Object>();
        int fieldCount = metaData.getFieldCount();

        Type type = method.getGenericReturnType();

        // 获取具体结果元素类型
        Class<?> elementType;
        // 获取泛型参数, 上面已经判断, array, iterator 为 many 因此这里只获取第一个泛型参数
        if (type instanceof ParameterizedType) {
            Type ar = ((ParameterizedType) type).getActualTypeArguments()[0];
            if (!Class.class.isAssignableFrom(ar.getClass())) {
                throw new IllegalStateException("unable to get results of specific types");
            }
            elementType = Class.class.cast(ar);
        } else if (type instanceof Class && ((Class<?>) type).isArray()) {
            elementType = ((Class<?>) type).getComponentType();
        } else {
            throw new IllegalStateException("unable to get results of specific types");
        }

        // 遍历所有结果行
        while (table.nextRow()) {
            // 封装结果
            Object rowObj = elementType.newInstance();

            // 遍历结果列
            for (int i = 0; i < fieldCount; i++) {
                String name = metaData.getName(i);
                Object value = table.getValue(name);  // 这里 JCO 已经转换为相应类型, 爽呀

                /*
                char[] chars = name.toLowerCase().toCharArray();
                chars[0] = Character.toUpperCase(chars[0]);
                Method setter = elementType.getMethod("set" + String.valueOf(chars), value.getClass());
                */
                Method setter = elementType.getMethod("set" + StringUtils.underscoreToCamelCase(name, true), value.getClass());
                setter.invoke(rowObj, value);
            }

            result.add(rowObj);
        }

        // 如果返回类型 不是 List
        Class<?> retType = method.getReturnType();
        if (!retType.isAssignableFrom(result.getClass())) {
            if (retType.isArray()) {
                // convert to array
                return toArray(result);
            } else {
                // convert to many type
                Class<?> clazz = resolveInterface(retType);
                throw new UnsupportedOperationException("only support array, list, collection, iterator result type now.");
            }
        }

        return result;
    }

    /**
     * 将 List 转换为当前代理方法的返回值类型的数组
     *
     * @param list
     * @param <E>
     * @return
     */
    protected <E> E[] toArray(List<E> list) {
        E[] array = (E[]) Array.newInstance(method.getReturnType().getComponentType(), list.size());
        return list.toArray(array);
    }

    /**
     * 解析接口为实现类, 如果不支持直接返回
     *
     * @param clazz
     * @return
     */
    protected Class<?> resolveInterface(Class<?> clazz) {
        if (List.class == clazz || Collection.class == clazz || Iterable.class == clazz) {
            return ArrayList.class;
        } else if (Map.class == clazz) {
            return HashMap.class;
        } else if (SortedSet.class == clazz) {
            return TreeSet.class;
        } else if (Set.class == clazz) {
            return HashSet.class;
        } else {
            return clazz;
        }
    }
}
